#include "recv.h"
void *tcpfunc(void *arg)
{
    int i, maxfd, n;
    fd_set rdfs, rdtmp;
    char buf[N] = {0};
    //强制类型转换
    struct sockaddr *soft_addr_ptr = (struct sockaddr *)arg;
    //创建tcp连接
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket err");
        return NULL;
    }
    //绑定结构体
    struct sockaddr_in saddr, caddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(TCP_PORT);             //填充端口
    saddr.sin_addr.s_addr = inet_addr("0.0.0.0"); //填充ip
    //重用套接字，断开连接后重新启用
    int on = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    
    if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
    {
        perror("bind is err in tcp1:");
        return NULL;
    }
    if (listen(sockfd, 32) < 0)
    {
        perror("listen err:");
        return NULL;
    }

    printf("listen success\n");

    // 3.更新最大文件描述符，并设置关注集
    maxfd = sockfd;
    FD_ZERO(&rdfs);        // 清空关注列表
    FD_SET(sockfd, &rdfs); // 将fd放入关注列表中
    socklen_t len = sizeof(caddr);
    while (1)
    {
        rdtmp = rdfs;
        // 5.调用select进行监听
        if (select(maxfd + 1, &rdtmp, NULL, NULL, NULL) < 0)
        {
            perror("select err");
            exit(-1);
        }
        // 遍历集合，如果服务器句柄有数据，那么代表有连接进来，把它加入描述符集并更新maxfd
        // 否则代表客户端句柄有数据，那么接收数据，如果对端退出，那么把它从描述符集清除
        for (i = 0; i < maxfd + 1; i++)
        {
            if (FD_ISSET(i, &rdtmp))
            {
                if (i == sockfd)
                { // 表示有新连接进来
                    connfd = accept(sockfd, (struct sockaddr *)soft_addr_ptr, &len);
                    printf("accept success\n");
                    FD_SET(connfd, &rdfs);
                    if (connfd > maxfd)
                    {
                        maxfd = connfd;
                    }
                }
                else
                { // 客户端有数据
                    bzero(buf, N);
                    n = recv(i, buf, sizeof(buf), 0);
                    if (n > 0)
                    {
                        // 反序列化
                        cJSON *root = cJSON_Parse(buf);
                        if (NULL == root)
                        {
                            printf("parse err\n");
                        }
                        cJSON *item = cJSON_GetObjectItem(root, "type");

                        printf("%s : %d\n", item->string, item->valueint);

                        if (item->valueint == 1)
                        {
                            //信息序列化
                            char *json_str = type2Json();
                            char buf[N] = {0};
                            strcpy(buf, json_str);
                            printf("%s\n",buf);
                            send(connfd, buf, sizeof(buf), 0);
                            printf("发送确定下发点表成功a\n");
                            free(json_str);
                        }
                        else if (item->valueint == 2)
                        {
                            // 新建一个新的服务器，这里如果还想保证8888的端口客户端正常响应，必须开线程，否则会阻塞到新的操作中
                            pthread_t tid;
                            pthread_create(&tid, NULL, new_sock, NULL);
                            send(connfd, "start", 6, 0); 
                        }
                        else if (item->valueint == 3)
                        {
                            printf("心跳包\n"); 
                        }
                    }
                }
            }

            
        }
    }
    close(connfd);
    close(sockfd);

    return NULL;
}

char *type2Json()
{
    cJSON *root, *data, *mqtt_config, *video_config, *update_config;

    root = cJSON_CreateObject();
    cJSON_AddNumberToObject(root, "type", 1);
    cJSON_AddNumberToObject(root, "result", 0);
    cJSON_AddBoolToObject(root, "up_config", true);

    data = cJSON_CreateObject();
    mqtt_config = cJSON_CreateObject();
    cJSON_AddStringToObject(mqtt_config, "mqtt_addr", "192.168.0.53");
    cJSON_AddNumberToObject(mqtt_config, "mqtt_port", 1883);
    cJSON_AddItemToObject(data, "mqtt_config", mqtt_config);

    video_config = cJSON_CreateObject();
    cJSON_AddStringToObject(video_config, "video_addr", "192.168.8.8");
    cJSON_AddNumberToObject(video_config, "video_port", 8888);
    cJSON_AddItemToObject(data, "video_config", video_config);

    update_config = cJSON_CreateObject();
    cJSON_AddNumberToObject(update_config, "type", 1);
    cJSON_AddNumberToObject(update_config, "period", 5);
    cJSON_AddItemToObject(data, "update_config", update_config);

    cJSON_AddItemToObject(root, "data", data);

    char *json_str = cJSON_Print(root);

    cJSON_Delete(root);
    return json_str;
}


void *new_sock(void *arg)
{
    printf("will recv new connect\n");
    int sockfd, clientfd;
    // char buf[N] = {0};
    socklen_t addrlen = sizeof(struct sockaddr);
    struct sockaddr_in addr, clientaddr;
    int nbytes;
    // 1创建一个套接字--socket
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket err");
        exit(-1);
    }

    // 2定义套接字地址--sockaddr_in
    bzero(&addr, addrlen);
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(12341); 
    //重用套接字，断开连接后重新启用
    int on = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    // 3绑定套接字--bind
    if (bind(sockfd, (struct sockaddr *)&addr, addrlen) < 0)
    {
        perror("bind err in tcp2");
        
    }

    // 4启动监听--listen
    if (listen(sockfd, 5) < 0)
    {
        perror("listen err");
        exit(-1);
    }

    // 5接收连接--accept
    clientfd = accept(sockfd,  (struct sockaddr *)&clientaddr, &addrlen);
    if (clientfd < 0)
    {
        perror("accept err");
        exit(-1);
    }

    printf("recv new client\n");
    char nodebuf[2658]={0};
    // 6收发数据--recv/send
    while (1) {
            nbytes = recv(clientfd, nodebuf, 2658, 0);
            if(nbytes < 0) {
                    perror("recv err");
                    exit(-1);
            } else if (nbytes > 0) {
                    printf("%s\n", nodebuf);
                    send(connfd,"stop",4,0);
                    saveNodeJson(nodebuf);
                    
            } else {
                    printf("peer exit\n");
                    break;
            }
    }
    close(clientfd);
    close(sockfd);
    return NULL;
}


void saveNodeJson(char *nodebuf)
{
    FILE * fp = fopen("node.json","w");
    if(NULL == fp)
    {
        perror("fopen err");
    }
    if(fp != NULL)
    {
        fputs(nodebuf,fp);
    }
    printf("点表已保存于node.json文件中\n");
    fclose(fp);
}